﻿using System.Collections.Generic;
using System.Linq;
using FluentParser;
using FluentParser.Expressions.Rules.Decoration;
using FluentParser.Expressions;
using FluentParser.Utils;

namespace FluentParser.Editor
{
    public class JsonValue
    {
        
    }

    public class JsonBoolean : JsonValue
    {
        public bool Value { get; private set; }

        public JsonBoolean(bool value)
        {
            Value = value;
        }
    }

    public class JsonNumber : JsonValue
    {
        public int Value { get; private set; }

        public JsonNumber(int value)
        {
            Value = value;
        }
    }

    public class JsonString : JsonValue
    {
        public string Value { get; private set; }

        public JsonString(string value)
        {
            Value = value;
        }
    }

    public class JsonArray : JsonValue
    {
        public JsonValue[] Values { get; set; }

        public JsonArray(IEnumerable<JsonValue> values)
        {
            Values = values.ToArray();
        }
    }

    public class JsonObject : JsonValue
    {
        public Dictionary<JsonString, JsonValue> Values { get; set; }

        public JsonObject()
        {
            Values = new Dictionary<JsonString, JsonValue>();
        }

        public JsonObject(Dictionary<JsonString, JsonValue> values)
        {
            Values = values;
        }

        public JsonObject(IEnumerable<Tuple<JsonString, JsonValue>> tuples)
        {
            Values = new Dictionary<JsonString, JsonValue>();
            foreach (Tuple<JsonString, JsonValue> tuple in tuples)
                Values.Add(tuple.Item1, tuple.Item2);
        }
    }

    public class JsonParserGrammar : Grammar<JsonValue>
    {
        public JsonParserGrammar()
        {
            // Valueless terminals
            Symbol spaces = ParseSymbol.Regex("[ ]*");
            Symbol comma = spaces + "," + spaces;
            Symbol colon = spaces + ":" + spaces;
            Symbol leftBracket = "[" + spaces;
            Symbol rightBracket = spaces + "]";
            Symbol leftAccolades = "{" + spaces;
            Symbol rightAccolades = spaces + "}";

            // Terminals
            Expr<bool> jTrue   = Symbol("true").Convert(x => true);
            Expr<bool> jFalse  = Symbol("false").Convert(x => false);
            Expr<JsonBoolean> jBoolean = (jTrue | jFalse).Convert(x => new JsonBoolean(x));
            Expr<JsonValue> jNull   = Symbol("null").Convert(x => new JsonValue());

            Expr<JsonString> jString = ParseString.Regex("(\\\"|[^\"])*")
                .Convert(text => new JsonString(text));

            Expr<JsonNumber> jNumber = ParseNumber.Int32
                .Convert(number => new JsonNumber(number));

            // Nonterminals
            FutureRule<JsonValue> jValue  = new FutureRule<JsonValue>();

            var jArray  = (leftBracket + jValue.Repeat(0, comma) + rightBracket)
                .Convert(array => new JsonArray(array));

            var jProperty = jString.Join(colon + jValue);

            var jObject = jProperty.Repeat(0, comma)
                .Surround(leftAccolades, rightAccolades)
                .Convert(tuples => new JsonObject(tuples));

            jValue.Source = jNull
                          | jBoolean.Cast<JsonValue>()
                          | jString.Cast<JsonValue>()
                          | jNumber.Cast<JsonValue>()
                          | jArray.Cast<JsonValue>()
                          | jObject.Cast<JsonValue>();

            Root = spaces + jValue + spaces;
        }
    }
}
